package org.viettan.android.main.view;

import java.lang.ref.WeakReference;
import java.net.URL;

import org.viettan.android.main.R;
import org.viettan.android.main.io.NetworkUtility;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.text.Html;
import android.util.Pair;
import android.view.View;
import android.webkit.WebView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.internal.Nullable;

/**
 * Provides utility methods to deal with views.
 */
public class ViewUtility {

    private final NetworkUtility networkUtility;

    // this is a bit of a hack to keep this static, but we don't expect screen resolution to change
    // that often
    private static int displayWidth;

    @Inject
    ViewUtility(NetworkUtility networkUtility) {
	this.networkUtility = networkUtility;
    }

    public void setImage(View view, int imageViewResourceID, int drawableResourceId) {
	ImageView imageView = (ImageView) view.findViewById(imageViewResourceID);
	imageView.setVisibility(ImageView.VISIBLE);
	imageView.setImageResource(drawableResourceId);
    }

    public void setImage(Activity activity, View view, int tagId, boolean scaleToDeviceWidth,
	    @Nullable Uri imageUrl) {
	ImageView imageView = (ImageView) view.findViewById(tagId);
	setImage(activity, imageView, scaleToDeviceWidth, imageUrl);
    }

    public void setImage(Activity activity, int tagId, boolean scaleToDeviceWidth,
	    @Nullable Uri imageUrl) {
	ImageView imageView = (ImageView) activity.findViewById(tagId);
	setImage(activity, imageView, scaleToDeviceWidth, imageUrl);
    }

    private void setImage(Activity activity, ImageView imageView, boolean scaleToDeviceWidth,
	    @Nullable Uri imageUrl) {
	Preconditions.checkState(imageView != null, "ImageView cannot be null");

	if (imageUrl == null) {
	    imageView.setImageResource(R.drawable.viettan_monochrome);
	    return;
	}

	displayWidth = activity.getWindowManager().getDefaultDisplay().getWidth();

	// download image in the background
	new DownloadImageTask(networkUtility, imageView, scaleToDeviceWidth, imageUrl)
		.execute();
    }

    public void scaleImageToDeviceWidth(ImageView imageView) {
	imageView.setImageBitmap(getImageScaledToDeviceWidth(imageView, imageView.getDrawable()));
    }

    private static Bitmap getImageScaledToDeviceWidth(ImageView imageView, Drawable originalImage) {
	Bitmap originalBitmap = ((BitmapDrawable) originalImage).getBitmap();

	// Get scaling factor to fit the max possible width of the ImageView
	float scalingFactor = getBitmapScalingFactor(imageView, originalBitmap);

	// fail-safe in case device width and max is not right (this happened based on our
	// crash reports)
	if (scalingFactor <= 0) {
	    scalingFactor = 1;
	}

	// Create a new bitmap with the scaling factor
	return scaleBitmap(originalBitmap, scalingFactor);
    }

    private static Bitmap scaleBitmap(Bitmap bm, float scalingFactor) {
	int scaleHeight = (int) (bm.getHeight() * scalingFactor);
	int scaleWidth = (int) (bm.getWidth() * scalingFactor);
	return Bitmap.createScaledBitmap(bm, scaleWidth, scaleHeight, true);
    }

    private static float getBitmapScalingFactor(ImageView imageView, Bitmap bm) {
	// Get margin to use it for calculating to max width of the ImageView
	LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) imageView
		.getLayoutParams();
	int leftMargin = layoutParams.leftMargin;
	int rightMargin = layoutParams.rightMargin;

	// Calculate the max width of the imageView
	int imageViewWidth = displayWidth - (leftMargin + rightMargin);

	// Calculate scaling factor and return it
	return (imageViewWidth / (float) bm.getWidth());
    }

    public void setText(View view, int tagId, String text) {
	TextView textView = (TextView) view.findViewById(tagId);
	setText(textView, text);
    }

    public void setText(Activity activity, int tagId, String text) {
	TextView textView = (TextView) activity.findViewById(tagId);
	setText(textView, text);
    }

    private void setText(TextView textView, String text) {
	Preconditions.checkState(textView != null, "TextView cannot be null");
	textView.setText(Html.fromHtml(Strings.nullToEmpty(text)));
    }

    public void setHtml(Activity activity, int tagId, String html) {
	WebView webView = (WebView) activity.findViewById(tagId);
	webView.loadDataWithBaseURL(null, html, "text/html", "utf-8", "about:blank");
    }

    /**
     * Returns the time in a human-friendly display of M:SS.
     *
     * @param milliseconds
     *            total milliseconds
     * @return
     */
    public static String getTime(int milliseconds) {
	int seconds = (milliseconds / 1000) % 60;
	int minutes = ((milliseconds / (1000 * 60)) % 60);
	return String.format("%d:%02d", minutes, seconds);
    }

    /**
     * A helper class that downloads an image from a given URL on a background thread and once
     * downloaded it displays the image onto a given {@link ImageView}.
     * <p>
     * As parameters to {@link #execute(Pair...)} pass in a single {@link Pair} containing first the
     * {@link ImageView} to draw on and secondly the {@link URL} to download the image from.
     */
    private static class DownloadImageTask extends
	    AsyncTask<Void, Void, Pair<WeakReference<ImageView>, Drawable>> {

	private final WeakReference<ImageView> imageViewReference;
	private final Uri imageUrl;
	private final NetworkUtility networkUtility;
	private final boolean scaleToDeviceWidth;

	private DownloadImageTask(NetworkUtility networkUtility,
		ImageView imageView, boolean scaleToDeviceWidth, Uri imageUrl) {
	    // This is a bit of a hack. Because we re-use ImageViews as the user scrolls through
	    // a list of articles, slow-loading images end up overwriting previously-loaded images
	    // when the user scrolls really fast. Download requests that "lag behind" end up
	    // overwriting images that are already loaded. To avoid this race condition, we'll
	    // "overload" content description has an indicator as to what URL we "meant" to be
	    // displaying
	    imageView.setContentDescription(imageUrl.toString());
	    this.networkUtility = networkUtility;
	    this.scaleToDeviceWidth = scaleToDeviceWidth;
	    this.imageUrl = imageUrl;
	    this.imageViewReference = new WeakReference<ImageView>(imageView);
	}

	@Override
	protected Pair<WeakReference<ImageView>, Drawable> doInBackground(Void... ignored) {
	    ImageView imageView = imageViewReference.get();
	    if (imageView == null) {
		return null;
	    }

	    return new Pair<WeakReference<ImageView>, Drawable>(imageViewReference,
		    networkUtility.getDrawableFromUrl(imageUrl));
	}

	@Override
	protected void onPostExecute(Pair<WeakReference<ImageView>, Drawable> imageViewAndDrawable) {
	    if (imageViewAndDrawable == null) {
		// not sure how this can be null, but per our crash reports this is happening
		return;
	    }
	    if (imageViewAndDrawable.second == null) {
		return;
	    }

	    ImageView imageView = imageViewAndDrawable.first.get();
	    if (imageView == null) {
		return;
	    }

	    // by the time we finish download an image, this imageView might have already been
	    // re-used to display another picture (imageViews are re-used in a scrolling list), so
	    // only display that downloaded image if it was meant for the imageView.
	    if (!imageView.getContentDescription().toString().equals(imageUrl.toString())) {
		return;
	    }
	    imageView.setVisibility(ImageView.VISIBLE);
	    if (scaleToDeviceWidth) {
		// see http://stackoverflow.com/questions/4877694/scaling-imageview-to-device-width
		imageView.setImageBitmap(getImageScaledToDeviceWidth(
			imageViewAndDrawable.first.get(),
			imageViewAndDrawable.second));
	    } else {
		imageView.setImageDrawable(imageViewAndDrawable.second);
	    }
	}

    }
}
